package com.jianbo.proxy.util;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import com.jianbo.proxy.Start;
import com.jianbo.proxy.conf.ArgsConf;

public class JsUtil {
    
    public static Object run(String code) {
        return runParam(code, (String)null);
    }

    private static String[][] importJava = {
        { "console", "com.jianbo.proxy.util.Console" }
    };

    private static String[][] importJs = {
        { "require", "com/jianbo/proxy/js/require.js" },
        { "ejs", "com/jianbo/proxy/js/ejs.js" },
        { "global", "com/jianbo/proxy/js/global.js" },
    };

    public static String readJson(String file) {
        String jsCode = FileUtil.readString(file);
        jsCode = outputJsCode(jsCode);
        return (String)JsUtil.runFull(jsCode);
    }

    public static String readJson(String file, String ... imports) {
        String jsCode = FileUtil.readString(file);
        jsCode = outputJsCode(jsCode);
        return (String)JsUtil.run(jsCode, imports);
    }

    public static String outputJsCode(String content) {
        content = content.replace("module.exports =", "var object = ");
        content = content + ResourceUtil.readString("com/jianbo/proxy/js/output_json.js");
        return content;
    }
    public static Object runFull(String code) {
        return run(code, "console", "require", "ejs", "global");
    }

    public static Object run(String code, String ... imports) {
        Map<String, String> javaMap = ArrayUtil.toMap(importJava);
        Map<String, String> javaScriptMap = ArrayUtil.toMap(importJs);
        Set<String> unimport = new HashSet<>();
        unimport.addAll(Arrays.asList(imports));

        String importCode = "";
        for(String module : imports) {
            if(javaMap.containsKey(module)) {
                String javaModule = javaMap.get(module);
                String javaModuleCode = "var "+module+" = Java.type(\""+javaModule+"\");\n";
                importCode = importCode + "\n" + javaModuleCode;
                unimport.remove(module);
            }
            if(javaScriptMap.containsKey(module)) {
                String scriptModuleFile = javaScriptMap.get(module);
                String scriptCode = ResourceUtil.readString(scriptModuleFile);
                importCode = importCode + "\n" + scriptCode + "\n";
                unimport.remove(module);
            }
        }

        // 引入 JavaApi
        importCode = "var JavaApi = Java.type(\"com.jianbo.proxy.util.JavaApi\");\n"
            + importCode;

        // 引入系统变量
        Map<String, String> data = new HashMap<>();
        ArgsConf argsConf = ArgsConf.getInstance();
        data.put("frontDir", argsConf.getFrontDir());
        data.put("confDir", argsConf.getConfDir());
        data.put("port", String.valueOf(Start.port));
        String json = JsonUtil.toJson(data);
        String jsonObj = "var System = " + json + ";\n";

        importCode = jsonObj
            + importCode;

        if(unimport.size() > 0) {
            throw new UnsupportedOperationException("不支持的模块：" + JsonUtil.toJson(imports));
        }
        code = importCode + "\n" + code;
        return runParam(code, (String)null);
    }

    private static ScriptEngine globalEngine;
    public static ScriptEngine getGlobalEngine() {
        if(globalEngine != null) {
            return globalEngine;
        }
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("javascript");
        globalEngine = engine;
        return engine;
    }
    
    /**
     * 执行js代码，拼接的方式实现参数传入
     * @param code
     * @param param
     * @return
     */
    public static Object runParam(String code, String param) {
        // ScriptEngineManager manager = new ScriptEngineManager();
        // ScriptEngine engine = manager.getEngineByName("javascript");
        ScriptEngine engine = getGlobalEngine();
        if(param != null) {
            String paramJs = "var param = " + param + ";";
            code = paramJs + code;
        }
        try {
            // Debug.info("Run Code Length:" + code.length());
            // Debug.info("Run Code Start:", code);
            // Debug.info("Run Code End");
            TimeUse.start("runParam");
            Object result = engine.eval(code);
            TimeUse.end("runParam");
            return result;
        } catch (ScriptException e) {
            Debug.info("Run code ERROR：", Debug.addLineNumber(code));
            throw new RuntimeException(e);
        }
    }

}
